#ifndef UTILS_H
#define UTILS_H

#include "anime.h"
#include <map>

namespace anime
{
    /* ---------------- PropertyList --------------------------------------- */
    template <class T>
    class PropertyList
    {
    public:
        typedef Property<T> PType;
        typedef vector< PType* > PVector;

        PropertyList(){}

        virtual ~PropertyList()
        {
            ClearProperties();
        }

        PType* AddSimpleProperty(const string& name, T& val, bool* invalideFlag = 0)
        {
            PType* p = new SimpleProperty<T>(name, &val, invalideFlag);
            v.push_back(p);
            return p;
        }

        PType* AddProperty(const string& name, PropertyGetter<T>* getter,
                         PropertySetter<T>* setter, bool* invalideFlag = 0)
        {
            PType* p = new PType(name, getter, setter, invalideFlag);
            v.push_back(p);
            return p;
        }

        void AddSimpleProperties(string* names, int** vals, int pCount, bool* invalideFlag = 0)
        {
            for(int i=0; i<pCount; i++)
                v.push_back(new SimpleProperty<T>(names[i], vals[i], invalideFlag));
        }

        void Delete(const string& name)
        {
            bool found = false;
            typename PVector::iterator i = v.begin();
            while(!found && i!=v.end())
            {
                found = (name == (*i)->GetName());
                ++i;
            }

            if(found)
            {
                --i;
                PType* toDelete = (*i);
                v.erase(i);
                delete toDelete;
            }
        }

        PType* Get(const string& name)
        {
            bool found = false;
            typename PVector::iterator i = v.begin();
            while(!found && i!=v.end())
            {
                found = (name == (*i)->GetName());
                ++i;
            }

            if(found)
            {
                --i;
                return (*i);
            }
            return 0;
        }

        virtual void ClearProperties()
        {
            for(typename PVector::iterator i=v.begin(); i!=v.end(); ++i)
                delete (*i);

            v.clear();
        }

    protected:
        PVector v;
    };
    /* --------------------------------------------------------------------- */

    /* -------------- Scalar_IS_List --------------------------------------- */
    template <class A, class B, class NormType = float>
    class ScalarInterpolators
    {
    public:
        typedef ScalarInterpolator<A, B, NormType> IType;
        typedef map< int, IType* > IMap;

        ScalarInterpolators():id(0){}

        virtual ~ScalarInterpolators()
        {
            ClearInterpolators();
        }

        int CreateInterpolator(NormalizedInterpolator<NormType>* n_is,
            const A& min_a, const A& max_a, const B& min_b, const B& max_b,
            bool deleteNI = false)
        {
            m[id] = (new IType(n_is, min_a, max_a, min_b, max_b, deleteNI));
            return id++;
        }

        IType* Get(int s_id)
        {
            typename IMap::iterator i = m.find(s_id);
            if (i == m.end()) return 0;
            return ((*i).second);
        }

        virtual void ClearInterpolators()
        {
            for(typename IMap::iterator i = m.begin(); i != m.end(); ++i)
            {
                delete ((*i).second);
            }
            m.clear();
        }

    protected:
        IMap m;
        int id;
    };
    /* --------------------------------------------------------------------- */

    /* --------------- IGraphBuilder --------------------------------------- */
    template <class PType, class NormType = float> class OneTypeScalarGraph
        :public PropertyList< PType >
    {
    public:
        typedef Property < PType > P;
        typedef PropertyList< PType > PList;
        typedef ScalarInterpolator< PType, PType, NormType> IType;
        typedef ScalarInterpolators< PType, PType, NormType > IList;

        OneTypeScalarGraph(){}
        virtual ~OneTypeScalarGraph(){}

        int AddInterpolator(const string& p1_name, PType min_x, PType max_x,
                            const string& p2_name, PType min_y, PType max_y,
                            NormalizedInterpolator< NormType >* ni = 0, bool disabled = false)
        {
            P* p1 = PList::Get(p1_name);
            if(p1 == 0) return -1;

            P* p2 = PList::Get(p2_name);
            if(p2 == 0) return -1;

            return AddInterpolator(p1, min_x, max_x, p2, min_y, max_y, ni, disabled);
        }

        IType* Get(int i_id){ return iList.Get(i_id); }
        P* Get(const string& name){ return PList::Get(name); }

        int AddInterpolator(P* p1, PType min_x, PType max_x,
            const string &p2_name, PType min_y, PType max_y,
            NormalizedInterpolator<NormType> *ni = 0, bool disabled = false)
        {
            P* p2 = PList::Get(p2_name);
            if(p2 == 0) return -1;

            return AddInterpolator(p1, min_x, max_x, p2, min_y, max_y, ni, disabled);
        }

        int AddInterpolator(P* p1, PType min_x, PType max_x, P* p2, PType min_y, PType max_y,
                             NormalizedInterpolator<NormType> *ni = 0, bool disabled = false)
        {
            NormalizedInterpolator< NormType >* n = ni;
            if(n == 0)
            {
                n = new Linear_NI< NormType >();
            }

            int i_id = iList.CreateInterpolator(n, min_x, max_x, min_y, max_y, (ni == 0));
            IType* i = iList.Get(i_id);
            i->SetEnabled(!disabled);

            i->AttachTo(p2);
            p1->AddObserver(i);
            return i_id;
        }

        void ClearInterpolators(){ iList.ClearInterpolators(); }

    private:
        IList iList;
    };

    typedef OneTypeScalarGraph<int> IntGraph;
    typedef OneTypeScalarGraph< float > FloatGraph;
    /* --------------------------------------------------------------------- */

    /* --------------- Pulse ----------------------------------------------- */
    class Pulse;

    class PulseObserver
    {
    public:
        virtual void OnPulse(int pulseIndex) = 0;
    };

    class Pulse: public Observable<PulseObserver>
    {
    public:
        Pulse(int pulses, Property<int>* source, int min_t, int max_t)
        {
            src = source;

            spg = new SimplePropertyGetter<int>(&dummy);
            ps = new PulseSetter(this->observers);
            pulseProperty = new Property<int>("pulse property", spg, ps);

            si = new ScalarInterpolator<int, int>(&li, min_t, max_t, 0, pulses, false);
            si->AttachTo(pulseProperty);
            src->AddObserver(si);
        }

        virtual ~Pulse()
        {
            delete spg;
            delete ps;
            delete pulseProperty;
            delete si;
        }

        void SetPulseCount(int count){ si->SetMaxY(count); ps->Reset(); }
        void SetSourceMax(int srcMax){ si->SetMaxX(srcMax); }
        void Enable(){ si->Enable(); }
        void Disable(){ si->Disable(); }
        void Reset(){ ps->Reset(); }
        void SetEnabled(bool set){ si->SetEnabled(set); }

    private:
        class PulseSetter: public PropertySetter<int>
        {
        public:
            PulseSetter(vector< PulseObserver* >* _obs):pp(-1), obs(_obs){}
            ~PulseSetter(){}

            virtual void operator()(const int& val)
            {
                if(val != pp)
                {
                    pp = val;
                    for(vector <PulseObserver*>::iterator i=obs->begin(); i!=obs->end(); i++)
                    {
                        (*i)->OnPulse(pp);
                    }
                }
            }

            void Reset(){ pp = -1; }

        private:
            int pp;
            vector< PulseObserver* >* obs;
        };

        // fields
        Property<int>* src;
        Linear_NI<float> li;
        int dummy;

        SimplePropertyGetter<int>* spg;
        PulseSetter* ps;
        Property<int>* pulseProperty;
        ScalarInterpolator<int, int>* si;
    };
    /* --------------------------------------------------------------------- */
}

#endif // UTILS_H
